﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;

namespace CAION2.Helpers
{
    public class GridAnimationBehavior : DependencyObject
    {
        #region Attached IsExpanded DependencyProperty

        public static readonly DependencyProperty IsExpandedProperty =
          DependencyProperty.RegisterAttached("IsExpanded", typeof(bool), typeof(GridAnimationBehavior),
            new FrameworkPropertyMetadata(OnIsExpandedChanged));

        public static void SetIsExpanded(DependencyObject dependencyObject, bool value)
        {
            dependencyObject.SetValue(IsExpandedProperty, value);
        }

        #endregion

        #region Attached Duration DependencyProperty

        public static readonly DependencyProperty DurationProperty =
          DependencyProperty.RegisterAttached("Duration", typeof(TimeSpan), typeof(GridAnimationBehavior),
            new FrameworkPropertyMetadata(TimeSpan.FromMilliseconds(200)));

        public static void SetDuration(DependencyObject dependencyObject, TimeSpan value)
        {
            dependencyObject.SetValue(DurationProperty, value);
        }

        private static TimeSpan GetDuration(DependencyObject dependencyObject)
        {
            return (TimeSpan)dependencyObject.GetValue(DurationProperty);
        }

        #endregion

        #region GridCellSize DependencyProperty

        private static readonly DependencyProperty GridCellSizeProperty =
          DependencyProperty.Register("GridCellSize", typeof(double), typeof(GridAnimationBehavior),
            new UIPropertyMetadata(0.0));

        private static void SetGridCellSize(DependencyObject dependencyObject, double value)
        {
            dependencyObject.SetValue(GridCellSizeProperty, value);
        }

        private static double GetGridCellSize(DependencyObject dependencyObject)
        {
            return (double)dependencyObject.GetValue(GridCellSizeProperty);
        }

        #endregion

        private static void OnIsExpandedChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var duration = GetDuration(dependencyObject);
            var rowDefinition = dependencyObject as RowDefinition;
            if (rowDefinition != null)
            {
                if ((bool)e.NewValue)
                {
                    var expandedHeight = GetGridCellSize(rowDefinition);
                    if (expandedHeight > 0)
                    {
                        AnimateGridRowExpandCollapse(rowDefinition, true, expandedHeight, rowDefinition.ActualHeight, 0, duration.Seconds, duration.Milliseconds);
                    }
                }
                else
                {
                    SetGridCellSize(rowDefinition, rowDefinition.ActualHeight);
                    AnimateGridRowExpandCollapse(rowDefinition, false, rowDefinition.ActualHeight, 0, 0, duration.Seconds, duration.Milliseconds);
                }
            }

            var columnDefinition = dependencyObject as ColumnDefinition;
           
            if (columnDefinition == null) return;
            if ((bool)e.NewValue)
            {
                var expandedWidth = GetGridCellSize(columnDefinition);
                if (expandedWidth > 0)
                {
                    AnimateGridColumnExpandCollapse(columnDefinition, true, expandedWidth, columnDefinition.ActualWidth, 0, duration.Seconds, duration.Milliseconds);
                }
            }
            else
            {
                SetGridCellSize(columnDefinition, columnDefinition.ActualWidth);
                AnimateGridColumnExpandCollapse(columnDefinition, false, columnDefinition.ActualWidth, 0, 0, duration.Seconds, duration.Milliseconds);
            }
        }

        public static void AnimateGridColumnExpandCollapse(ColumnDefinition gridColumn, bool expand, double expandedWidth, 
            double collapsedWidth, double minWidth, int seconds, int milliseconds)
        {
            if (expand && gridColumn.ActualWidth >= expandedWidth) return;

            if (!expand && gridColumn.ActualWidth == collapsedWidth) return;

            Storyboard storyBoard = new Storyboard();

            GridLengthAnimation animation = new GridLengthAnimation(GridUnitType.Auto)
                {
                    From = new GridLength(gridColumn.ActualWidth),
                    To = new GridLength(expand ? expandedWidth : collapsedWidth),
                    Duration = new TimeSpan(0, 0, 0, seconds, milliseconds)
                };
            
            animation.Completed += delegate
            {
                gridColumn.BeginAnimation(ColumnDefinition.WidthProperty, null);
                gridColumn.Width = new GridLength(expand ? expandedWidth : collapsedWidth);
                gridColumn.MinWidth = minWidth;
            };

            storyBoard.Children.Add(animation);

            Storyboard.SetTarget(animation, gridColumn);
            Storyboard.SetTargetProperty(animation, new PropertyPath(ColumnDefinition.WidthProperty));
            storyBoard.Children.Add(animation);
            
            storyBoard.Begin();
        }


        public static void AnimateGridRowExpandCollapse(RowDefinition gridRow, bool expand, double expandedHeight, 
            double collapsedHeight, double minHeight, int seconds, int milliseconds)
        {
            if (expand && gridRow.ActualHeight >= expandedHeight) return;

            if (!expand && gridRow.ActualHeight == collapsedHeight) return;

            Storyboard storyBoard = new Storyboard();

            GridLengthAnimation animation = new GridLengthAnimation(GridUnitType.Auto)
                {
                    From = new GridLength(gridRow.ActualHeight),
                    To = new GridLength(expand ? expandedHeight : collapsedHeight),
                    Duration = new TimeSpan(0, 0, 0, seconds, milliseconds)
                };
            
            animation.Completed += delegate
            {
                gridRow.BeginAnimation(RowDefinition.HeightProperty, null);
                gridRow.Height = new GridLength(expand ? expandedHeight : collapsedHeight);
                gridRow.MinHeight = minHeight;
            };

            storyBoard.Children.Add(animation);

            Storyboard.SetTarget(animation, gridRow);
            Storyboard.SetTargetProperty(animation, new PropertyPath(RowDefinition.HeightProperty));
            storyBoard.Children.Add(animation);

            storyBoard.Begin();
        }
    }

    /// <summary>
    /// This work is based on an article of Christian Graus and Nishant Sivakumar.
    /// More information at : http://www.codeproject.com/KB/WPF/GridLengthAnimation.aspx
    /// </summary>
    sealed public class GridLengthAnimation : AnimationTimeline
    {

        #region Variables

        private GridUnitType _unitType;

        public static readonly DependencyProperty FromProperty;
        public static readonly DependencyProperty ToProperty;

        #endregion

        #region Constructor

        static GridLengthAnimation()
        {
            FromProperty = DependencyProperty.Register("From", typeof(GridLength),
                typeof(GridLengthAnimation));

            ToProperty = DependencyProperty.Register("To", typeof(GridLength),
                typeof(GridLengthAnimation));
        }

        public GridLengthAnimation(GridUnitType unitType)
        {
            _unitType = unitType;
        }

        #endregion

        #region CreateInstanceCore

        protected override System.Windows.Freezable CreateInstanceCore()
        {
            return new GridLengthAnimation(GridUnitType.Pixel);
        }

        #endregion

        #region Properties

        public override Type TargetPropertyType
        {
            get
            {
                return typeof(GridLength);
            }
        }

        public GridLength From
        {
            get
            {
                return (GridLength)GetValue(GridLengthAnimation.FromProperty);
            }
            set
            {
                SetValue(GridLengthAnimation.FromProperty, value);
            }
        }

        public GridLength To
        {
            get
            {
                return (GridLength)GetValue(GridLengthAnimation.ToProperty);
            }
            set
            {
                SetValue(GridLengthAnimation.ToProperty, value);
            }
        }

        #endregion

        #region GetCurrentValue

        /// <summary>
        /// The magic is here, it is a simple interpolation.
        /// </summary>
        public override object GetCurrentValue(object defaultOriginValue, object defaultDestinationValue, AnimationClock animationClock)
        {
            double fromVal = ((GridLength)GetValue(GridLengthAnimation.FromProperty)).Value;
            double toVal = ((GridLength)GetValue(GridLengthAnimation.ToProperty)).Value;

            if (fromVal > toVal)
                return new GridLength((1 - animationClock.CurrentProgress.Value) * (fromVal - toVal) + toVal, _unitType);

            return new GridLength(animationClock.CurrentProgress.Value * (toVal - fromVal) + fromVal, _unitType);
        }

        #endregion

    }

}
